{---------------------------------------------------------------------
#
#	Apple Macintosh Developer Technical Support
#
#	MultiFinder-Aware Simple TextEdit Sample Application
#
#	OOPTESample
#
#	UTEDocument.inc1.p	-	Pascal Source
#
#	Copyright © 1988, 1989 Apple Computer, Inc.
#	All rights reserved.
#
#	Version:		
#					1.00					04/89
#
#	Components:     
#					BuildOOPTESample		April 1, 1989
#					MOOPTESample.p			April 1, 1989
#					OOPTESample.make		April 1, 1989
#					TECommon.h				April 1, 1989
#					TESampleGlue.a			April 1, 1989
#					TESample.r				April 1, 1989
#					UApplication.p			April 1, 1989
#					UApplication.inc1.p		April 1, 1989
#					UDocument.p				April 1, 1989
#					UDocument.inc1.p		April 1, 1989
#					UTEDocument.p			April 1, 1989
#					UTEDocument.inc1.p		April 1, 1989
#					UTESample.p				April 1, 1989
#					UTESample.inc1.p		April 1, 1989
#
---------------------------------------------------------------------}

CONST
	kTextMargin				= 2;	{kTextMargin is the number of pixels we leave
									blank at the edge of the window.}

	kMaxDocWidth			= 576;	{kMaxDocWidth is an arbitrary number used to specify
									the width of the TERec's destination rectangle so that
									word wrap and horizontal scrolling can be demonstrated.}


	kMinDocDim				= 64;	{kMinDocDim is used to limit the minimum dimension
									of a window when GrowWindow is called.}


	kControlInvisible		= 0;
	kControlVisible			= $FF;	{kControlInvisible is used to 'turn off' controls
									(i.e., cause the control not to be redrawn as a result
									of some Control Manager call such as SetCtlValue)
									by being put into the contrlVis field of the record.
									kControlVisible is used the same way to 'turn on'
									the control.}

	kScrollbarWidth			= 16;	{kScrollBarAdjust and kScrollBarWidth are used in
									calculating values for control positioning and sizing.}
	kScrollbarAdjust		= kScrollbarWidth - 1;
	kGrowboxAdjust 			= 15;

	kScrollTweek			= 2;	{kScrollTweek compensates for off-by-one requirements
									of the scrollbars to have borders coincide with the
									growbox.}

	kCRChar					= chr(13);
	kDelChar				= chr(8);	{kCrChar is used to match with a carriage return
									when calculating the number of lines in the TextEdit
									record. kDelChar is used to check for delete in
									keyDowns.}

	kButtonScroll			= 4;	{kButtonScroll is how many pixels to scroll horizontally
									when the button part of the horizontal scrollbar is
									pressed.}


	kMaxTELength			= 32000;	{kMaxTELength is an arbitrary number used to limit
									the length of text in the TERec so that various errors
									won't occur from too many characters in the text.}

	kTESlop					= 1024;	{ provides some extra security when pre-flighting
									edit commands. }

	rVScroll				= 128;	{ vertical scrollbar control }
	rHScroll				= 129;	{ horizontal scrollbar control }

	kTEDocErrStrings	 	= 129;	{ id of our STR# for error strings }

	{ The following are indicies into STR# resources. }
	eNoMemory				= 1;
	eNoSpaceCut				= 2;
	eNoCut					= 3;
	eNoCopy					= 4;
	eExceedPaste			= 5;
	eNoSpacePaste			= 6;
	eNoWindow				= 7;
	eExceedChar				= 8;
	eNoPaste				= 9;

(********************************************************************************************)
(*		G l o b a l   R o u t i n e s														*)
(********************************************************************************************)
(*
	Routines used by this class, which don't belong to the class since we use
	them as toolbox filter routines, and you cannot pass class methods as ProcPtrs.
*)


PROCEDURE AsmClikLoop; EXTERNAL;

{ Common algorithm for pinning the value of a control. It returns the actual amount }
{ the value of the control changed. }

{$S Main}
PROCEDURE CommonAction(control:ControlHandle; VAR amount:integer);
VAR
	value, max: integer;
BEGIN
	value := GetCtlValue(control);
	max := GetCtlMax(control);
	amount := value - amount;
	IF (amount <= 0) THEN
		amount := 0
	ELSE IF (amount >= max) THEN
		amount := max;
	SetCtlValue(control, amount);
	amount := value - amount;   { calculate true change }
END; { CommonAction  }


{ Determines how much to change the value of the vertical scrollbar by and how }
{ much to scroll the TE record.}

{$S Main}
PROCEDURE VActionProc(control:ControlHandle;part:integer);
VAR
	amount:integer;
	window:WindowPtr;
	hTE:TEHandle;
	doc:TTEDocument;
BEGIN
	IF (part <> 0) THEN BEGIN
		window := control^^.contrlOwner;
		doc := TTEDocument(gApplication.DocList.FindDoc(window));
		hTE := doc.GetTEHandle;
		CASE part OF
			inUpButton, inDownButton:		{ one line  }
				amount := 1;
			inPageUp, inPageDown:			{ one page  }
				WITH hTE^^,viewRect DO
					amount := (bottom - top) DIV lineHeight;
		END;
		IF ((part = inDownButton) OR (part = inPageDown)) THEN
			amount := -amount;		{ reverse direction for a downer  }
		CommonAction(control, amount);
		IF (amount <> 0) THEN
			TEScroll(0, amount*hTE^^.lineHeight, hTE);
	END;
END; { VActionProc }

{ Determines how much to change the value of the horizontal scrollbar by and how }
{ much to scroll the TE record. }

{$S Main}
PROCEDURE HActionProc(control:ControlHandle;part:integer);
VAR
	amount:integer;
	window:WindowPtr;
	hTE:TEHandle;
	doc:TTEDocument;
BEGIN
	IF (part <> 0) THEN BEGIN
		window := control^^.contrlOwner;
		doc := TTEDocument(gApplication.DocList.FindDoc(window));
		hTE := doc.GetTEHandle;
		CASE part OF
			inUpButton, inDownButton:		{ a few pixels }
				amount := kButtonScroll;
			inPageUp, inPageDown:			{ a page width }
				WITH hTE^^.viewRect DO
					amount := (right - left);
		END;
		IF ((part = inDownButton) OR (part = inPageDown)) THEN
			amount := -amount;		{ reverse direction }
		CommonAction(control, amount);
		IF (amount <> 0) THEN
			TEScroll(amount, 0, hTE);
	END;
END; { HActionProc }

{ Gets called from our assembly language routine, AsmClikLoop, which in turn }
{ is called by the TEClick toolbox routine. Saves the window's clip region, }
{ sets it to the portRect, adjusts the scrollbar values to match the TE scroll }
{ amount, then restores the clip region. }

{$S Main}
PROCEDURE PascalClikLoop;
VAR
	region: RgnHandle;
	wind: WindowPtr;
	doc: TTEDocument;
BEGIN
	wind := FrontWindow;
	doc := TTEDocument(gApplication.DocList.FindDoc(wind));
	region := NewRgn;
	GetClip(region);				{ save the old clip }
	ClipRect(wind^.portRect);		{ set the new clip }
	doc.AdjustScrollValues(FALSE);	{ pass false for canRedraw }
	SetClip(region);				{ restore the old clip }
	DisposeRgn(region);
END; { PascalClikLoop }

{ Gets called from our assembly language routine, AsmClikLoop, which is in }
{ turn called by the TEClick toolbox routine. It returns the address of the }
{ default clikLoop routine that was put into the TERec by TEAutoView to }
{ AsmClikLoop so that it can call it. }

{$S Main}
FUNCTION GetOldClikLoop:ProcPtr;
VAR
	doc: TTEDocument;
BEGIN
	doc := TTEDocument(gApplication.DocList.FindDoc(FrontWindow));
	IF (doc = NIL) THEN
		GetOldClikLoop := nil
	ELSE
		GetOldClikLoop := doc.GetClikLoop;
END; { GetOldClikLoop }


(********************************************************************************************)
(*		T T E D o c u m e n t																*)
(********************************************************************************************)

{$S Initialize}
{-----------------------------------+
|	ITEDocument						|
+-----------------------------------}
PROCEDURE TTEDocument.ITEDocument(resID:integer);
VAR
	good:Boolean;
	destRect, viewRect: Rect;
BEGIN

	IDocument(resID);

	SetPort(fDocWindow);
	GetTERect(viewRect);
	destRect := viewRect;
	destRect.right := destRect.left + kMaxDocWidth;
	fDocTE := TENew(destRect, viewRect);

	good := (fDocTE <> NIL);		{ if TENew succeeded, we have a good document. }
	IF good THEN BEGIN				{ good document? Ñ get scrollbars  }
		AdjustViewRect;
		TEAutoView(TRUE, fDocTE);
		fDocClik := fDocTE^^.clikLoop;
		fDocTE^^.clikLoop := @AsmClikLoop;
		fDocVScroll := GetNewControl(rVScroll, fDocWindow);
		good := (fDocVScroll <> NIL);
	END;
	IF good THEN BEGIN
		fDocHScroll := GetNewControl(rHScroll, fDocWindow);
		good := (fDocHScroll <> NIL);
	END;

	IF good THEN BEGIN				{ good? Ñ adjust & draw the controls, draw the window }
		AdjustScrollValues(FALSE);
		ShowWindow(fDocWindow);
	END ELSE BEGIN					{ tell user we failed }
		AlertUser(kTEDocErrStrings,eNoWindow);
	END;
END;

{$S Main}
{-----------------------------------+
|	Free							|
+-----------------------------------}
PROCEDURE TTEDocument.Free; OVERRIDE;
BEGIN
	HideWindow(fDocWindow);
	IF fDocTE <> NIL THEN
	  TEDispose(fDocTE);			{ dispose the TEHandle if we got far enough to make one  }
	IF fDocVScroll <> NIL THEN
	  DisposeControl(fDocVScroll);
	IF fDocHScroll <> NIL THEN
	  DisposeControl(fDocHScroll);
	INHERITED Free;
END;

{$S Main}
{-----------------------------------+
|	DoZoom							|
+-----------------------------------}
PROCEDURE TTEDocument.DoZoom(partCode:integer); OVERRIDE;
BEGIN
	EraseRect(fDocWindow^.portRect);
	ZoomWindow(fDocWindow, partCode, (fDocWindow = FrontWindow));
	ResizeWindow;	{after this, only thing valid is scrollbars.}
END;

{$S Main}
{-----------------------------------+
|	DoGrow							|
+-----------------------------------}

{Called when a mouseDown occurs in the grow box of an active window. In
 order to eliminate any 'flicker', we want to invalidate only what is
 necessary. Since ResizeWindow invalidates the whole portRect, we save
 the old TE viewRect, intersect it with the new TE viewRect, and
 remove the result from the update region. However, we must make sure
 that any old update region that might have been around gets put back.}

PROCEDURE TTEDocument.DoGrow(theEvent:EventRecord); OVERRIDE;
VAR
	growResult: longint;
	tempRect:	Rect;
	tempRgn:	RgnHandle;
	
	PROCEDURE GetLocalUpdateRgn(aRgn:RgnHandle);
	BEGIN
		CopyRgn(WindowPeek(fDocWindow)^.updateRgn, aRgn);	{save old update region}
		WITH fDocWindow^.portBits.bounds DO
			OffsetRgn(aRgn, left, top);						{convert to local coords}
	END;

BEGIN
	tempRect := screenBits.bounds;
	tempRect.left := kMinDocDim;
	tempRect.top := kMinDocDim;
	growResult := GrowWindow(fDocWindow, theEvent.where, tempRect);
	{ see if it really changed size  }
	IF growResult <> 0 THEN BEGIN
		tempRect := fDocTE^^.viewRect;
		tempRgn := NewRgn;
		GetLocalUpdateRgn(tempRgn);
		SizeWindow(fDocWindow, LoWrd(growResult), HiWrd(growResult), TRUE);
		ResizeWindow;	{after this, only thing valid is scrollbars.}
		IF SectRect(tempRect, fDocTE^^.viewRect, tempRect) THEN;
		ValidRect(tempRect);
		InvalRgn(tempRgn);
		DisposeRgn(tempRgn);
	END;
END;

{$S Main}
{-----------------------------------+
|	DoContent						|
+-----------------------------------}
PROCEDURE TTEDocument.DoContent(theEvent:EventRecord); OVERRIDE;
VAR
	mouse:		Point;
	control:	ControlHandle;
	part,value:	integer;
	shiftDown:	Boolean;
BEGIN
	SetPort(fDocWindow);
	mouse := theEvent.where;			{ get the click position  }
	GlobalToLocal(mouse);
	part := FindControl(mouse, fDocWindow, control);
	CASE part OF
	  	0: BEGIN						{ not in a control }
			{ see if we need to extend the selection  }
			shiftDown := BAnd(theEvent.modifiers, shiftKey) <> 0;	{ extend if Shift is down  }
			TEClick(mouse, shiftDown, fDocTE);
		END;
		inThumb: BEGIN
			value := GetCtlValue(control);
			part := TrackControl(control, mouse, NIL);
			IF part <> 0 THEN BEGIN
				value := value - GetCtlValue(control);
				{ value now has CHANGE in value; if value changed, scroll  }
				IF value <> 0 THEN BEGIN
					IF control = fDocVScroll THEN BEGIN
						TEScroll(0, value * fDocTE^^.lineHeight, fDocTE);
					END ELSE BEGIN
						TEScroll(value, 0, fDocTE);
					END;
				END;
			END;
		END;
		OTHERWISE BEGIN						{ they clicked in an arrow, so track & scroll  }
			IF control = fDocVScroll THEN BEGIN
				value := TrackControl(control, mouse, @VActionProc);
			END ELSE BEGIN
				value := TrackControl(control, mouse, @HActionProc);
			END;
		END;
	END;
END;

{$S Main}
{-----------------------------------+
|	DoKeyDown						|
+-----------------------------------}
PROCEDURE TTEDocument.DoKeyDown(theEvent:EventRecord); OVERRIDE;
VAR
	key: char;
BEGIN
	IF BAnd(theEvent.modifiers, cmdKey) <> 1 THEN BEGIN	{ don't process command characters }
		key := char(BAnd(theEvent.message, charCodeMask));
		{ we have a char. for our window; see if we are still below TextEditÕs }
		{ limit for the number of characters }
		WITH fdocTE^^ DO BEGIN
			IF ((key = kDelChar) OR ((teLength - selEnd - selStart) + 1 < kMaxTELength)) THEN BEGIN
				TEKey(key, fDocTE);
				AdjustScrollbars(FALSE);
				AdjustTE;
			END ELSE BEGIN
				AlertUser(kTEDocErrStrings,eExceedChar);
			END;
		END;
	END;
END;

{$S Main}
{-----------------------------------+
|	DoActivate						|
+-----------------------------------}
PROCEDURE TTEDocument.DoActivate(becomingActive:Boolean); OVERRIDE;
VAR
	tempRgn:	RgnHandle;
	clipRgn:	RgnHandle;
	growRect:	Rect;
BEGIN
	IF becomingActive THEN BEGIN

		{ since we donÕt want TEActivate to draw a selection in an area where }
		{ weÕre going to erase and redraw, weÕll clip out the update region }
		{ before calling it. }
		tempRgn := NewRgn;
		clipRgn := NewRgn;
		{ save old update region }
		CopyRgn(WindowPeek(fDocWindow)^.updateRgn, tempRgn);
		{ put it in local coords }
		WITH fDocWindow^.portBits.bounds DO
			OffsetRgn(tempRgn, left, top);
		GetClip(clipRgn);
		{ subtract updateRgn from clipRgn }
		DiffRgn(clipRgn, tempRgn, tempRgn);
		{ make it the new clipRgn }
		SetClip(tempRgn);
		TEActivate(fDocTE);
		{ restore the full-blown clipRgn }
		SetClip(clipRgn);
		{ get rid of temp regions }
		DisposeRgn(tempRgn);
		DisposeRgn(clipRgn);

		{ the controls must be redrawn on activation: }
		fDocVScroll^^.contrlVis := kControlVisible;
		fDocHScroll^^.contrlVis := kControlVisible;
		InvalRect(fDocVScroll^^.contrlRect);
		InvalRect(fDocHScroll^^.contrlRect);
		{ the growbox needs to be redrawn on activation: }
		growRect := fDocWindow^.portRect;
		{ adjust for the scrollbars }
		WITH growRect DO BEGIN
			top := bottom - kScrollbarAdjust;
			left := right - kScrollbarAdjust;
		END;
		InvalRect(growRect);
	END ELSE BEGIN				{ becoming Inactive }
		TEDeactivate(fDocTE);
		{ the controls must be hidden on deactivation: }
		HideControl(fDocVScroll);
		HideControl(fDocHScroll);
		{ we draw grow icon immediately, since we deactivate controls }
		{ immediately, and the update delay looks funny }
		DrawGrowIcon(fDocWindow);
	END;
END;

{$S Main}
{-----------------------------------+
|	DoIdle							|
+-----------------------------------}
PROCEDURE TTEDocument.DoIdle; OVERRIDE;
BEGIN
	TEIdle(fDocTE);
END;

{$S Main}
{-----------------------------------+
|	DoUpdate						|
+-----------------------------------}
PROCEDURE TTEDocument.DoUpdate; OVERRIDE;
BEGIN
	BeginUpdate(fDocWindow);					{ this sets up the visRgn }
	IF NOT EmptyRgn(fDocWindow^.visRgn) THEN	{ draw if updating needs to be done }
		DrawWindow;
	EndUpdate(fDocWindow);
END;

{$S Main}
{-----------------------------------+
|	DoCut							|
+-----------------------------------}
PROCEDURE TTEDocument.DoCut; OVERRIDE;
VAR
	total, contig:	longint;
BEGIN
	IF (ZeroScrap = noErr) THEN BEGIN
		PurgeSpace(total, contig);
		IF (fDocTE^^.selEnd - fDocTE^^.selStart + kTESlop > contig) THEN BEGIN
			AlertUser(kTEDocErrStrings,eNoSpaceCut);
		END ELSE BEGIN
			TECut(fDocTE);
			IF (TEToScrap <> noErr) THEN BEGIN
				AlertUser(kTEDocErrStrings,eNoCut);
				IF Boolean(ZeroScrap) THEN;
			END;
		END;
	END;
	AdjustScrollbars(FALSE);
	AdjustTE;
END;

{$S Main}
{-----------------------------------+
|	DoCopy							|
+-----------------------------------}
PROCEDURE TTEDocument.DoCopy; OVERRIDE;
BEGIN
	IF (ZeroScrap = noErr) THEN BEGIN
		TECopy(fDocTE);				{ after copying, export the TE scrap }
		IF (TEToScrap <> noErr) THEN BEGIN
			AlertUser(kTEDocErrStrings,eNoCopy);
			IF Boolean(ZeroScrap) THEN;
		END;
	END;
	AdjustScrollbars(FALSE);
	AdjustTE;
END;

{$S Main}
{-----------------------------------+
|	DoPaste							|
+-----------------------------------}
PROCEDURE TTEDocument.DoPaste; OVERRIDE;
VAR
	aHandle:	Handle;
	oldsize,
	newSize:	longint;
	saveErr:	OSErr;
BEGIN
	IF (TEFromScrap = noErr) THEN BEGIN
		WITH fDocTE^^ DO BEGIN
			IF (TEGetScrapLen + (teLength - (selEnd - selStart)) > kMaxTELength) THEN BEGIN
				AlertUser(kTEDocErrStrings,eExceedPaste);
			END ELSE BEGIN
				aHandle := Handle(TEGetText(fDocTE));
				oldSize := GetHandleSize(aHandle);
				newSize := oldSize + TEGetScrapLen + kTESlop;
				SetHandleSize(aHandle, newSize);
				saveErr := MemError;
				SetHandleSize(aHandle, oldSize);
				IF (saveErr <> noErr) THEN BEGIN
					AlertUser(kTEDocErrStrings,eNoSpacePaste);
				END ELSE BEGIN
					TEPaste(fDocTE);
				END;
			END;
		END;
	END ELSE BEGIN
		AlertUser(kTEDocErrStrings,eNoPaste);
	END;
	AdjustScrollbars(FALSE);
	AdjustTE;
END;

{$S Main}
{-----------------------------------+
|	DoClear							|
+-----------------------------------}
PROCEDURE TTEDocument.DoClear; OVERRIDE;
BEGIN
	TEDelete(fDocTE);
	AdjustScrollbars(FALSE);
	AdjustTE;
END;

{$S Main}
{-----------------------------------+
|	HaveSelection					|
+-----------------------------------}
FUNCTION TTEDocument.HaveSelection:Boolean; OVERRIDE;
BEGIN
	IF (fDocTE^^.selStart < fDocTE^^.selEnd) THEN
		HaveSelection := TRUE
	ELSE
		HaveSelection := FALSE;
END;

{$S Main}
{-----------------------------------+
|	CalcIdle						|
+-----------------------------------}
FUNCTION TTEDocument.CalcIdle:Longint; OVERRIDE;
BEGIN
	IF NOT(HaveSelection) THEN BEGIN
		CalcIdle := GetCaretTime;
	END ELSE BEGIN
		CalcIdle := $7FFFFFFF;
	END;
END;

{$S Main}
{-----------------------------------+
|	AdjustScrollValues				|
+-----------------------------------}
{ Simply call the common adjust routine for the vertical and horizontal scrollbars. }
PROCEDURE TTEDocument.AdjustScrollValues(mustRedraw:Boolean);
BEGIN
	AdjustHV(true, mustRedraw);
	AdjustHV(false, mustRedraw);
END;

{$S Main}
{-----------------------------------+
|	GetClikLoop						|
+-----------------------------------}
FUNCTION TTEDocument.GetClikLoop:ProcPtr;
BEGIN
	GetClikLoop := fDocClik;
END;

{$S Main}
{-----------------------------------+
|	GetTEHandle						|
+-----------------------------------}
FUNCTION TTEDocument.GetTEHandle:TEHandle;
BEGIN
	GetTEHandle := fDocTE;
END;

{$S Main}
{-----------------------------------+
|	GetVisTERgn						|
+-----------------------------------}
PROCEDURE TTEDocument.GetVisTERgn(rgn:RgnHandle);
VAR
	teRect:	Rect;
BEGIN
	teRect := fDocTE^^.viewRect;		{ get a local copy of viewRect }
	SetPort(fDocWindow);			{ make sure we have right port }
	LocalToGlobal(teRect.topLeft);
	LocalToGlobal(teRect.botRight);
	RectRgn(rgn, teRect);
	{ we temporarily change the portÕs origin to ÒglobalfyÓ the visRgn }
	WITH fDocWindow^.portbits.bounds DO
		SetOrigin(-left,-top);
	SectRgn(rgn, fDocWindow^.visRgn, rgn);
	SetOrigin(0, 0);
END;

{$S Main}
{-----------------------------------+
|	GetTERect						|
+-----------------------------------}
{ Return a rectangle that is inset from the portRect by the size of }
{ the scrollbars and a little extra margin. }
PROCEDURE TTEDocument.GetTERect(VAR teRect:Rect);
BEGIN
	teRect := fDocWindow^.portRect;
	InsetRect(teRect, kTextMargin, kTextMargin);		{ adjust for margin  }
	WITH teRect DO BEGIN
		bottom := bottom - kScrollbarAdjust;			{ and for the scrollbars }
		right := right - kScrollbarAdjust;
	END;
END;

{$S Main}
{-----------------------------------+
|	AdjustTE						|
+-----------------------------------}
{ Scroll the TERec around to match up to the potentially updated scrollbar }
{ values. This is really useful when the window has been resized such that the }
{ scrollbars became inactive but the TERec was already scrolled. }
PROCEDURE TTEDocument.AdjustTE;
BEGIN
	WITH fDocTE^^ DO BEGIN
		TEScroll((viewRect.left - destRect.left) - GetCtlValue(fDocHScroll),
				 (viewRect.top - destRect.top) - (GetCtlValue(fDocVScroll) * lineHeight),
				 fDocTE);
	END;
END;

{$S Main}
{-----------------------------------+
|	DrawWindow						|
+-----------------------------------}
PROCEDURE TTEDocument.DrawWindow;
BEGIN
	SetPort(fDocWindow);
	EraseRect(fDocWindow^.portRect);	{ As per TextEdit chapter of Inside Macintosh }
	DrawControls(fDocWindow);			{ This ordering makes for a better appearance }
	DrawGrowIcon(fDocWindow);
	TEUpdate(fDocWindow^.portRect, fDocTE);
END;

{$S Main}
{-----------------------------------+
|	AdjustViewRect					|
+-----------------------------------}
{ Update the TERec's view rect so that it is the greatest multiple of }
{ the lineHeight that still fits in the old viewRect. }
PROCEDURE TTEDocument.AdjustViewRect;
BEGIN
	WITH fDocTE^^,fDocTE^^.viewRect DO BEGIN
		bottom := (((bottom - top) DIV lineHeight) * lineHeight) + top;
	END;
END;

{$S Main}
{-----------------------------------+
|	ResizeWindow					|
+-----------------------------------}

{Called when the window has been resized to fix up the controls and content.
 This routine moves and resizes the scrollbars, adjusts their values, scrolls
 the TERecord in case we grew the window out from under it, invalidates the
 entire port, and then validates the areas under the scrollbars.}
 
PROCEDURE TTEDocument.ResizeWindow;
BEGIN
	AdjustScrollbars(TRUE);				{ adjust, redraw anyway  }
	AdjustTE;
	InvalRect(fDocWindow^.portRect);	{ invalidate the whole content  }
	{ the scrollbars were taken care of by AdjustScrollbars, so validate Õem  }
	ValidRect(fDocVScroll^^.contrlRect);
	ValidRect(fDocHScroll^^.contrlRect);
END;

{$S Main}
{-----------------------------------+
|	AdjustHV						|
+-----------------------------------}
{ Calculate the new control maximum value and current value, whether it is the horizontal or }
{ vertical scrollbar. The vertical max is calculated by comparing the number of lines to the }
{ vertical size of the viewRect. The horizontal max is calculated by comparing the maximum document }
{ width to the width of the viewRect. The current values are set by comparing the offset between }
{ the view and destination rects. If necessary, redraw the control by calling ShowControl. }
PROCEDURE TTEDocument.AdjustHV(isVert, mustRedraw:Boolean);
VAR
	value, lines, max:	integer;
	oldValue, oldMax:	integer;
	hTE:				TEHandle;
	control:			ControlHandle;
BEGIN
	IF isVert THEN
		control := fDocVScroll
	ELSE
		control := fDocHScroll;
	oldValue := GetCtlValue(control);
	oldMax := GetCtlMax(control);
	hTE := fDocTE;
	WITH hTE^^ DO BEGIN
		IF isVert THEN BEGIN
			lines := nLines;
			{ since nLines isnÕt right if the last character is a return, check for that case }
			{ MUST perform a short circuit check here, or we get a check error. }
			IF ((teLength > 0) & (CharsHandle(hText)^^[teLength-1] = kCrChar)) THEN
				lines := lines + 1;
			WITH viewRect DO max := lines - ((bottom - top) DIV lineHeight);
		END	ELSE BEGIN
			WITH viewRect DO max := kMaxDocWidth - (right - left);
		END;
	END;
	IF max < 0 THEN max := 0;
	SetCtlMax(control, max);
	
	WITH hTE^^ DO BEGIN
		IF isVert THEN BEGIN
			value := (viewRect.top - destRect.top) DIV lineHeight
		END ELSE BEGIN
			value := viewRect.left - destRect.left;
		END;
	END;
	
	{Pin the value to within range}
	IF value < 0 THEN value := 0;
	IF value > max THEN value := max;
	
	SetCtlValue(control, value);
	{ now redraw the control if asked to or if a setting changed  }
	IF ((mustRedraw) OR ((max <> oldMax) OR (value <> oldValue))) THEN
		ShowControl(control);
END;

{$S Main}
{-----------------------------------+
|	AdjustScrollSizes				|
+-----------------------------------}
{ Re-calculate the position and size of the viewRect and the scrollbars. }
{ kScrollTweek compensates for off-by-one requirements of the scrollbars }
{ to have borders coincide with the growbox. }
PROCEDURE TTEDocument.AdjustScrollSizes;
VAR
	teRect:	Rect;
BEGIN	
	GetTERect(teRect);
	fDocTE^^.viewRect := teRect;
	AdjustViewRect;
	WITH fDocWindow^.portRect DO BEGIN
		MoveControl(fDocVScroll, right - kScrollbarAdjust, -1);
		SizeControl(fDocVScroll, kScrollbarWidth, bottom - top - kGrowboxAdjust + kScrollTweek);
		MoveControl(fDocHScroll, -1, bottom - kScrollbarAdjust);
		SizeControl(fDocHScroll, right - left - kGrowboxAdjust + kScrollTweek, kScrollbarWidth);
	END;
END;

{$S Main}
{-----------------------------------+
|	AdjustScrollbars				|
+-----------------------------------}
{ Turn off the controls by jamming a zero into their contrlVis fields (HideControl erases them }
{ and we don't want that). If the controls are to be resized as well, call the procedure to do that, }
{ then call the procedure to adjust the maximum and current values. Finally re-enable the controls }
{ by jamming a $FF in their contrlVis fields (ShowControl re-draws the control, which may not be }
{ necessary). }
PROCEDURE TTEDocument.AdjustScrollbars(needsResize:Boolean);
BEGIN
	{ First, turn visibility of scrollbars off so we wonÕt get unwanted redrawing  }
	fDocVScroll^^.contrlVis := kControlInvisible;
	fDocHScroll^^.contrlVis := kControlInvisible;
	IF needsResize THEN BEGIN
		AdjustScrollSizes;
	END;
	AdjustScrollValues(needsResize);
	{ Now, restore visibility in case we never had to draw during adjustment  }
	fDocVScroll^^.contrlVis := kControlVisible;
	fDocHScroll^^.contrlVis := kControlVisible;
END;
